package cn.bytengine.stem.events;

import cn.bytengine.stem.functional.Consumers;
import cn.bytengine.stem.functional.invoker.InvokeException;
import cn.bytengine.stem.functional.invoker.Invoker;
import cn.bytengine.stem.functional.invoker.InvokerFactory;

import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;

public class BaseEventBus implements EventBus {
    private final ConcurrentMap<String, CopyOnWriteArraySet<Invoker>> subscriber = new ConcurrentHashMap<>();
    private final ConcurrentMap<Serializable, Invoker> invokerMapping = new ConcurrentHashMap<>();
    private final InvokerFactory invokerFactory;

    public BaseEventBus(InvokerFactory invokerFactory) {
        this.invokerFactory = invokerFactory;
    }

    @Override
    public void post(String eventName, Object... args) {
        // TODO 消息发送模式，选择消费者策略实现
        pub(eventName, args);
    }

    @Override
    public void pub(String eventName, Object... args) {
        CopyOnWriteArraySet<Invoker> invokers = subscriber.computeIfAbsent(eventName, en -> new CopyOnWriteArraySet<>());
        for (Invoker invoker : invokers) {
            invoke(invoker, eventName, args);
        }
    }

    protected void invoke(Invoker invoker, String eventName, Object[] args) {
        try {
            invoker.invoke(args);
        } catch (Throwable e) {
            throw new InvokeException(e);
        }
    }

    protected void register0(String eventName, Serializable serializable, RegisterOption[] options) {
        Invoker invoker = invokerFactory.create(serializable);
        for (RegisterOption option : options) {
            invoker = option.option(eventName, invoker);
        }
        invokerMapping.put(serializable, invoker);
        CopyOnWriteArraySet<Invoker> invokers = subscriber.computeIfAbsent(eventName, en -> new CopyOnWriteArraySet<>());
        invokers.add(invoker);
    }

    @Override
    public void unregister(String eventName, Serializable serializable) {
        if (invokerMapping.containsKey(serializable) && subscriber.containsKey(eventName)) {
            Invoker invoker = invokerMapping.get(serializable);
            subscriber.get(eventName).remove(invoker);
        }
    }

    @Override
    public EventBus register(String eventName, Consumers.Consumer0 consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1> EventBus register(String eventName, Consumers.Consumer1<T1> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2> EventBus register(String eventName, Consumers.Consumer2<T1, T2> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3> EventBus register(String eventName, Consumers.Consumer3<T1, T2, T3> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4> EventBus register(String eventName, Consumers.Consumer4<T1, T2, T3, T4> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5> EventBus register(String eventName, Consumers.Consumer5<T1, T2, T3, T4, T5> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6> EventBus register(String eventName, Consumers.Consumer6<T1, T2, T3, T4, T5, T6> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6, T7> EventBus register(String eventName, Consumers.Consumer7<T1, T2, T3, T4, T5, T6, T7> consumer, RegisterOption... options) {
        return null;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6, T7, T8> EventBus register(String eventName, Consumers.Consumer8<T1, T2, T3, T4, T5, T6, T7, T8> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6, T7, T8, T9> EventBus register(String eventName, Consumers.Consumer9<T1, T2, T3, T4, T5, T6, T7, T8, T9> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6, T7, T8, T9, TA> EventBus register(String eventName, Consumers.ConsumerA<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB> EventBus register(String eventName, Consumers.ConsumerB<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }

    @Override
    public <T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC> EventBus register(String eventName, Consumers.ConsumerC<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC> consumer, RegisterOption... options) {
        register0(eventName, consumer, options);
        return this;
    }
}

